package cn.jiedanba.cacert.common.ca.cert;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import cn.jiedanba.cacert.common.bc.base.BaseSecurity;
import cn.jiedanba.cacert.common.ca.PkiUtil;
import cn.jiedanba.cacert.common.ca.extension.ExtensionUtil;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCert;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCertCustomExtension;
import cn.jiedanba.cacert.common.ca.keypair.KeyPairUtil;
import cn.jiedanba.cacert.common.util.DateUtil;

public class PkiCertUtil extends BaseSecurity {
	/**
	 * 颁发根证书
	 * 
	 * @param subjectDn
	 *            颁发者，使用者
	 * @param serialNumber
	 *            证书序列号
	 * @param notBefore
	 *            证书生效时间
	 * @param notAfter
	 *            证书过期时间
	 * @param rootkeypair
	 *            密钥对
	 * @param signatureAlgorithm
	 *            证书签名算法
	 * @return
	 */
	public static X509Certificate buildRootCert(X500Name subjectDn, BigInteger serialNumber, Date notBefore,
			Date notAfter, KeyPair rootkeypair, String signatureAlgorithm) {
		try {
			ContentSigner sigGen = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(BC)
					.build(rootkeypair.getPrivate());
			// 颁发者
			X500Name issuerName = subjectDn;

			// 使用者
			X500Name subjectName = issuerName;

			// 秘钥用法
			int usage = X509KeyUsage.keyCertSign | X509KeyUsage.cRLSign;
			KeyUsage keyUsage = new KeyUsage(usage);

			// 基本限制,标识是否是CA证书
			Extension basicConstraints = ExtensionUtil.getBasicConstraintsExtension(true);

			// 使用者秘钥标识符
			Extension subjectKeyIdentifier = ExtensionUtil.getSubjectKeyIdentifierExtension(rootkeypair.getPublic());

			// 授权秘钥标识符
			Extension authorityKeyIdentifier = ExtensionUtil
					.getAuthorityKeyIdentifierExtension(rootkeypair.getPublic());

			// 构建证书
			X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore,
					notAfter, subjectName, rootkeypair.getPublic());

			certGen.addExtension(Extension.keyUsage, true, keyUsage);
			certGen.addExtension(basicConstraints);
			certGen.addExtension(subjectKeyIdentifier);
			certGen.addExtension(authorityKeyIdentifier);

			// 将证书构造参数装换为X.509证书对象
			X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BC)
					.getCertificate(certGen.build(sigGen));

			return certificate;
		} catch (Exception e) {
			throw new RuntimeException("根证书创建失败", e);
		}

	}

	/**
	 * 生成证书
	 * 
	 * @param applySubjectCert
	 * @return
	 */
	public static X509Certificate buildSubjectCert(PkiCert vo) {
		try {
			// 颁发者
			X500Name issuerName = new JcaX509CertificateHolder(vo.getIssuerCert()).getSubject();
			// 使用者
			X500Name subjectName = vo.getSubjectDN();
			// 构建证书
			X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(issuerName, vo.getSerialNumber(),
					vo.getStartTime(), vo.getEndTime(), subjectName, vo.getSubjectPublicKey());

			int keyU = 0;
			for (int u : vo.getUsage()) {
				keyU = +(keyU | u);
			}
			// 密钥基本用法
			KeyUsage keyUsage = new KeyUsage(keyU);

			// 基本限制,标识是否是CA证书
			Extension basicConstraints = ExtensionUtil.getBasicConstraintsExtension(vo.isCA());

			// 使用者秘钥标识符
			Extension subjectKeyIdentifier = ExtensionUtil.getSubjectKeyIdentifierExtension(vo.getSubjectPublicKey());

			// 授权秘钥标识符
			Extension authorityKeyIdentifier = ExtensionUtil
					.getAuthorityKeyIdentifierExtension(vo.getIssuerCert().getPublicKey());

			certGen.addExtension(Extension.keyUsage, true, keyUsage);
			certGen.addExtension(basicConstraints);
			certGen.addExtension(authorityKeyIdentifier);
			certGen.addExtension(subjectKeyIdentifier);

			if (vo.getExtensions() != null && vo.getExtensions().size() > 0) {
				for (PkiCertCustomExtension extension : vo.getExtensions()) {
					certGen.addExtension(ExtensionUtil.getExtendAttributes(extension.getOid(), extension.getValue()));
				}
			}

			// DNSNAME
			if (!ObjectUtils.isEmpty(vo.getDnsNames()) || !ObjectUtils.isEmpty(vo.getIps())) {
				TreeSet<String> dnsName = new TreeSet<>();
				if (!ObjectUtils.isEmpty(vo.getDnsNames())) {
					dnsName.addAll(vo.getDnsNames());
				}

				TreeSet<String> ips = new TreeSet<>();
				if (!ObjectUtils.isEmpty(vo.getIps())) {
					ips.addAll(vo.getIps());
				}

				Extension subjectAlternativeNameExtension = ExtensionUtil.getSubjectAlternativeNameExtension(dnsName,
						ips);
				certGen.addExtension(subjectAlternativeNameExtension);
			}

			// 密钥扩展用法
			if (vo.getExtendUsage() != null) {

				List<KeyPurposeId> keyPurposeIds = new ArrayList<KeyPurposeId>();
				for (String string : vo.getExtendUsage()) {
					ASN1ObjectIdentifier newKeyPurposeIdOID = new ASN1ObjectIdentifier(string);
					KeyPurposeId newKeyPurposeId = KeyPurposeId.getInstance(newKeyPurposeIdOID);
					keyPurposeIds.add(newKeyPurposeId);
				}
				ExtendedKeyUsage extendedKeyUsage = new ExtendedKeyUsage(
						keyPurposeIds.toArray(new KeyPurposeId[keyPurposeIds.size()]));
				Extension extension = null;
				extension = new Extension(Extension.extendedKeyUsage, true,
						extendedKeyUsage.toASN1Primitive().getEncoded());
				certGen.addExtension(extension);
			}
			// crl分发节点
			if (StringUtils.isNotBlank(vo.getCrl())) {
				certGen.addExtension(ExtensionUtil.getCRLDIstPointExtension(new String[] { vo.getCrl() }));
			}

			// ocsp和crt
			Extension authorityInfoAccess = ExtensionUtil.getAuthorityInfoAccessExtensionOCSPCRL(vo.getOscp(),
					vo.getCrt());
			if (authorityInfoAccess != null) {
				certGen.addExtension(authorityInfoAccess);
			}

			// 证书策略
			if (!ObjectUtils.isEmpty(vo.getPolicyVos())) {
				certGen.addExtension(ExtensionUtil.getCertificatePoliciesExtension(vo.getPolicyVos()));
			}

			X509Certificate resultCert = new JcaX509CertificateConverter()
					.getCertificate(certGen.build(new JcaContentSignerBuilder(vo.getSignatureAlgorithm())
							.setProvider(BC).build(vo.getIssuerPrivateKey())));
			return resultCert;
		} catch (Exception e) {
			throw new RuntimeException("生成证书失败", e);
		}

	}

	/**
	 * 构建证书主题
	 * 
	 * @param CN
	 * @param OU
	 * @param O
	 * @param L
	 * @param ST
	 * @param C
	 * @return X500Name
	 */
	public static X500Name buildName(String email, String CN, String OU, String O, String L, String ST, String C) {
		X500NameBuilder nameBuilder = new X500NameBuilder();
		if (StringUtils.isNotBlank(C)) {
			nameBuilder.addRDN(BCStyle.C, C);
		}

		if (StringUtils.isNotBlank(ST)) {
			nameBuilder.addRDN(BCStyle.ST, ST);
		}

		if (StringUtils.isNotBlank(L)) {
			nameBuilder.addRDN(BCStyle.L, L);
		}

		if (StringUtils.isNotBlank(OU)) {
			nameBuilder.addRDN(BCStyle.OU, OU);
		}

		if (StringUtils.isNotBlank(O)) {
			nameBuilder.addRDN(BCStyle.O, O);
		}

		if (StringUtils.isNotBlank(CN)) {
			nameBuilder.addRDN(BCStyle.CN, CN);
		}

		if (StringUtils.isNotBlank(email)) {
			nameBuilder.addRDN(BCStyle.EmailAddress, email);
		}

		return nameBuilder.build();
	}

	/**
	 * 构建证书主题
	 * 
	 * @param subjectDn
	 * @return
	 */
	public static X500Name buildName(String subjectDn) {
		String[] split = subjectDn.split(",");
		String email = null;
		String cn = null;
		String ou = null;
		String o = null;
		String l = null;
		String st = null;
		String c = null;
		for (String x : split) {
			if (x.contains("E=") || x.contains("e=")) {
				email = x.trim().replaceAll("E=", "").replaceAll("e=", "");
			} else if (x.contains("CN=") || x.contains("cn=")) {
				cn = x.trim().replaceAll("CN=", "").replaceAll("cn=", "");
			} else if (x.contains("OU=") || x.contains("ou=")) {
				ou = x.trim().replaceAll("OU=", "").replaceAll("ou=", "");
			} else if (x.contains("O=") || x.contains("o=")) {
				o = x.trim().replaceAll("O=", "").replaceAll("o=", "");
			} else if (x.contains("L=") || x.contains("l=")) {
				l = x.trim().replaceAll("L=", "").replaceAll("l=", "");
			} else if (x.contains("ST=") || x.contains("st=")) {
				st = x.trim().replaceAll("ST=", "").replaceAll("st=", "");
			} else if (x.contains("S=") || x.contains("s=")) {
				st = x.trim().replaceAll("S=", "").replaceAll("s=", "");
			} else if (x.contains("C=") || x.contains("c=")) {
				c = x.trim().replaceAll("C=", "").replaceAll("c=", "");

			}
		}

		return buildName(email, cn, ou, o, l, st, c);
	}

	public static void main(String[] args) throws Exception {
		KeyPair key = KeyPairUtil.generateKeyPair("RSA", 2048);
		String privateKey = PkiUtil.toPemObject("RSA PRIVATE KEY", key.getPrivate().getEncoded());
		System.out.println(privateKey);

		X509Certificate signCert = buildRootCert(buildName(null, "JSNPS", null, null, null, null, null),
				new BigInteger("1"), new Date(), DateUtil.getNextDay(new Date(), 7300), key, "SHA256WithRSA");

		System.out.println(PkiUtil.toPemObject("CERTIFICATE", signCert.getEncoded()));
	}

}
